#include "BasicTypeHelpers.hpp"

#include "ClassMeta.h"
#include "Field.h"
#include "Type.h"

namespace MOE
{
	namespace Core
	{
		namespace BasicTypeHelpers
		{
			// Implementations of Read/Write functions. No need to call these functions directly.
			namespace Impl
			{
				bool CopyClassMetaValueImpl(const ClassMeta* classMeta, BasicType typeEnumReq, tUntypedConstRef source, tUntypedRef target)
				{
					if (!classMeta || !source || !target)
					{
						return false;
					}
					if (!classMeta->GetBasicType() != typeEnumReq)
					{
						return false;
					}
					classMeta->CopyValue(source, target);
					return true;
				}

				bool ReadFieldValueImpl(const Field* field, BasicType typeEnumReq, tUntypedConstRef sourceObj, tUntypedRef target)
				{
					if (!field || !sourceObj || !target)
					{
						return false;
					}
					const Type& fieldType = field->GetType();
					if (fieldType.GetReferenceType() == ReferenceType::kReference || fieldType.GetArrayType() != ArrayType::kSingle)
					{
						return false;
					}
					const ClassMeta& fieldClassMeta = fieldType.GetClassMeta();
					if (fieldClassMeta.GetBasicType() != typeEnumReq)
					{
						return false;
					}
					field->ReadValue(sourceObj, target);
					return true;
				}

				bool WriteFieldValueImpl(const Field* field, BasicType typeEnumReq, tUntypedConstRef source, tUntypedRef targetObj)
				{
					if (!field || !source || !targetObj)
					{
						return false;
					}
					const Type& fieldType = field->GetType();
					if (fieldType.GetReferenceType() == ReferenceType::kReference || fieldType.GetArrayType() != ArrayType::kSingle)
					{
						return false;
					}
					const ClassMeta& fieldClassMeta = fieldType.GetClassMeta();
					if (fieldClassMeta.GetBasicType() != typeEnumReq)
					{
						return false;
					}
					field->WriteValue(source, targetObj);
					return true;
				}
			}

			// Specialization for:
			// template <typename T>
			// bool Read(const ClassMeta* classMeta, tUntypedConstRef source, T& outValue)

			template <>
			bool Read<int8>(const ClassMeta* classMeta, tUntypedConstRef source, int8& outValue)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kInt8, source, &outValue);
			}

			template <>
			bool Read<uint8>(const ClassMeta* classMeta, tUntypedConstRef source, uint8& outValue)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kUint8, source, &outValue);
			}

			template <>
			bool Read<int16>(const ClassMeta* classMeta, tUntypedConstRef source, int16& outValue)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kInt16, source, &outValue);
			}

			template <>
			bool Read<uint16>(const ClassMeta* classMeta, tUntypedConstRef source, uint16& outValue)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kUint16, source, &outValue);
			}

			template <>
			bool Read<int32>(const ClassMeta* classMeta, tUntypedConstRef source, int32& outValue)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kInt32, source, &outValue);
			}

			template <>
			bool Read<uint32>(const ClassMeta* classMeta, tUntypedConstRef source, uint32& outValue)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kUint32, source, &outValue);
			}

			template <>
			bool Read<int64>(const ClassMeta* classMeta, tUntypedConstRef source, int64& outValue)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kInt64, source, &outValue);
			}

			template <>
			bool Read<uint64>(const ClassMeta* classMeta, tUntypedConstRef source, uint64& outValue)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kUint64, source, &outValue);
			}

			// Specialization for:
			// template <typename T>
			// bool Write(const ClassMeta* classMeta, const T& value, tUntypedRef target)

			template <>
			bool Write<int8>(const ClassMeta* classMeta, const int8& value, tUntypedRef target)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kInt8, &value, target);
			}

			template <>
			bool Write<uint8>(const ClassMeta* classMeta, const uint8& value, tUntypedRef target)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kUint8, &value, target);
			}

			template <>
			bool Write<int16>(const ClassMeta* classMeta, const int16& value, tUntypedRef target)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kInt16, &value, target);
			}

			template <>
			bool Write<uint16>(const ClassMeta* classMeta, const uint16& value, tUntypedRef target)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kUint16, &value, target);
			}

			template <>
			bool Write<int32>(const ClassMeta* classMeta, const int32& value, tUntypedRef target)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kInt32, &value, target);
			}

			template <>
			bool Write<uint32>(const ClassMeta* classMeta, const uint32& value, tUntypedRef target)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kUint32, &value, target);
			}

			template <>
			bool Write<int64>(const ClassMeta* classMeta, const int64& value, tUntypedRef target)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kInt64, &value, target);
			}

			template <>
			bool Write<uint64>(const ClassMeta* classMeta, const uint64& value, tUntypedRef target)
			{
				return Impl::CopyClassMetaValueImpl(classMeta, BasicType::kUint64, &value, target);
			}

			// Specialization for:
			// template <typename T>
			// bool Read(const Field* field, tUntypedConstRef sourceObj, T& outValue)

			template <>
			bool Read<int8>(const Field* field, tUntypedConstRef sourceObj, int8& outValue)
			{
				return Impl::ReadFieldValueImpl(field, BasicType::kInt8, sourceObj, &outValue);
			}

			template <>
			bool Read<uint8>(const Field* field, tUntypedConstRef sourceObj, uint8& outValue)
			{
				return Impl::ReadFieldValueImpl(field, BasicType::kUint8, sourceObj, &outValue);
			}

			template <>
			bool Read<int16>(const Field* field, tUntypedConstRef sourceObj, int16& outValue)
			{
				return Impl::ReadFieldValueImpl(field, BasicType::kInt16, sourceObj, &outValue);
			}

			template <>
			bool Read<uint16>(const Field* field, tUntypedConstRef sourceObj, uint16& outValue)
			{
				return Impl::ReadFieldValueImpl(field, BasicType::kUint16, sourceObj, &outValue);
			}

			template <>
			bool Read<int32>(const Field* field, tUntypedConstRef sourceObj, int32& outValue)
			{
				return Impl::ReadFieldValueImpl(field, BasicType::kInt32, sourceObj, &outValue);
			}

			template <>
			bool Read<uint32>(const Field* field, tUntypedConstRef sourceObj, uint32& outValue)
			{
				return Impl::ReadFieldValueImpl(field, BasicType::kUint32, sourceObj, &outValue);
			}

			template <>
			bool Read<int64>(const Field* field, tUntypedConstRef sourceObj, int64& outValue)
			{
				return Impl::ReadFieldValueImpl(field, BasicType::kInt64, sourceObj, &outValue);
			}

			template <>
			bool Read<uint64>(const Field* field, tUntypedConstRef sourceObj, uint64& outValue)
			{
				return Impl::ReadFieldValueImpl(field, BasicType::kUint64, sourceObj, &outValue);
			}

			// Specialization for:
			// template <typename T>
			// bool Write(const Field* field, const T& value, tUntypedRef targetObj)

			template <>
			bool Write<int8>(const Field* field, const int8& value, tUntypedRef targetObj)
			{
				return Impl::WriteFieldValueImpl(field, BasicType::kInt8, &value, targetObj);
			}

			template <>
			bool Write<uint8>(const Field* field, const uint8& value, tUntypedRef targetObj)
			{
				return Impl::WriteFieldValueImpl(field, BasicType::kUint8, &value, targetObj);
			}

			template <>
			bool Write<int16>(const Field* field, const int16& value, tUntypedRef targetObj)
			{
				return Impl::WriteFieldValueImpl(field, BasicType::kInt16, &value, targetObj);
			}

			template <>
			bool Write<uint16>(const Field* field, const uint16& value, tUntypedRef targetObj)
			{
				return Impl::WriteFieldValueImpl(field, BasicType::kUint16, &value, targetObj);
			}

			template <>
			bool Write<int32>(const Field* field, const int32& value, tUntypedRef targetObj)
			{
				return Impl::WriteFieldValueImpl(field, BasicType::kInt32, &value, targetObj);
			}

			template <>
			bool Write<uint32>(const Field* field, const uint32& value, tUntypedRef targetObj)
			{
				return Impl::WriteFieldValueImpl(field, BasicType::kUint32, &value, targetObj);
			}

			template <>
			bool Write<int64>(const Field* field, const int64& value, tUntypedRef targetObj)
			{
				return Impl::WriteFieldValueImpl(field, BasicType::kInt64, &value, targetObj);
			}

			template <>
			bool Write<uint64>(const Field* field, const uint64& value, tUntypedRef targetObj)
			{
				return Impl::WriteFieldValueImpl(field, BasicType::kUint64, &value, targetObj);
			}
		}
	}
}
